#include "shared_setup.h"

int raw_getmetatable(lua_State *L)
{
    if (!lua_getmetatable(L,1))
        return 0;
    return 1;
}

int raw_setmetatable(lua_State *L)
{
    lua_settop(L,2);
    lua_setmetatable(L, 1);
    return 1;
}

int raw_getfenv(lua_State *L)
{
    lua_getfenv(L,1);
    return 1;
}

int raw_setfenv(lua_State *L)
{
    lua_setfenv(L,1);
    return 1;
}

static int myseekfunc(SDL_RWops *context, int offset, int whence)
{
    EmbeddedResource* res = (EmbeddedResource*)context->hidden.unknown.data1;
    switch(whence)
    {
        case SEEK_CUR:
            fseek(res->fp, offset, SEEK_CUR);
            break;
        case SEEK_END:
            if (res->endpoint == -1)
                fseek(res->fp, offset, SEEK_END);
            else
                fseek(res->fp, res->endpoint + offset, SEEK_SET);
            break;
        case SEEK_SET:
            fseek(res->fp, res->origin + offset, SEEK_SET);
            break;
    }
    return ftell(res->fp) - res->origin;
}

static int myreadfunc(SDL_RWops *context, void *ptr, int size, int maxnum)
{
    EmbeddedResource* res = (EmbeddedResource*)context->hidden.unknown.data1;

	size_t nread = fread(ptr, size, maxnum, res->fp);
	if (nread == 0 && ferror(context->hidden.stdio.fp))
		SDL_Error(SDL_EFREAD);
	return nread;
}

static int mywritefunc(SDL_RWops *context, const void *ptr, int size, int num)
{
    SDL_SetError("Attempt to write in read-only RWops");
    return -1;
}

static int myclosefunc(SDL_RWops *context)
{
    if(context->type != 0xdeadbeef)
    {
        SDL_SetError("Wrong kind of RWops for myclosefunc()");
        return -1;
    }

    EmbeddedResource* res = (EmbeddedResource*)context->hidden.unknown.data1;
    fclose(res->fp);
    res = NULL;
    free(context->hidden.unknown.data1);

    SDL_FreeRW(context);
    return 0;
}

SDL_RWops* GuacResourceRWops(lua_State *L, int idx)
{
    if (lua_isnil(L,idx))
        luaL_error(L, "file not found");
    SDL_RWops* myRW = SDL_AllocRW();

    myRW->seek = myseekfunc;
    myRW->read = myreadfunc;
    myRW->write = mywritefunc;
    myRW->close = myclosefunc;
    myRW->type = 0xdeadbeef;

    EmbeddedResource* res = (EmbeddedResource*)malloc(sizeof(EmbeddedResource));

    lua_getfield(L,idx,"path");
    res->fp = fopen(lua_tostring(L,-1), "rb");
    lua_getfield(L,idx,"offset");
    res->origin = luaL_optint(L,-1,0);
    lua_getfield(L,idx,"length");
    if (!lua_isnumber(L,-1))
        res->endpoint = -1;
    else
        res->endpoint = res->origin + lua_tointeger(L,-1);

    lua_pop(L,3);

    fseek(res->fp, res->origin, SEEK_SET);

    myRW->hidden.unknown.data1 = res;

    return myRW;
}

static char buf[1024];

const char* lua_Reader_RW(lua_State *L, void *data, size_t *size)
{
    *size = SDL_RWread((SDL_RWops*)data, buf, 1, sizeof(buf));
    if (size == 0)
        return NULL;
    else
        return buf;
}
